/*
 * Copyright (C) 2016 Nishant Srivastava
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package github.nisrulz.easydeviceinfo.base;

import ohos.agp.utils.TextTool;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.net.NetHandle;
import ohos.net.NetManager;
import ohos.security.SystemPermission;
import ohos.security.permission.NeedsPermission;
import ohos.telephony.RadioInfoManager;
import ohos.telephony.SignalInformation;
import ohos.telephony.SimInfoManager;
import ohos.telephony.TelephonyConstants;
import ohos.wifi.WifiDevice;
import ohos.wifi.WifiLinkedInfo;
import ohos.wifi.WifiUtils;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Optional;

import github.nisrulz.easydeviceinfo.common.EasyDeviceInfo;

/**
 * EasyNetwork Mod Class
 *
 * @since 2021-04-25
 */
public class EasyNetworkMod {
    private static final String SOCKET_EXCEPTION = "Socket Exception";
    private final Context context;

    /**
     * 实例化
     * <p>
     * Instantiates a new Easy  network mod.
     *
     * @param context the context
     */
    public EasyNetworkMod(final Context context) {
        this.context = context;
    }

    /**
     * Is wifi enabled.
     *
     * @return the boolean
     */
    public final boolean isWifiEnabled() {
        boolean wifiState = false;
        WifiDevice wifiDevice = WifiDevice.getInstance(context);

        if (wifiDevice != null) {
            wifiState = wifiDevice.isWifiActive();
        }
        return wifiState;
    }

    /**
     * Is network available boolean.
     * <p>
     * You need to declare the below permission in the manifest file to use this properly
     * <p>
     * SystemPermission.GET_WIFI_INFO
     * SystemPermission.INTERNET
     *
     * @return the boolean
     */
    @NeedsPermission(all = {
        SystemPermission.GET_WIFI_INFO, SystemPermission.INTERNET
    })
    public final boolean isNetworkAvailable() {
        if (PermissionUtil.hasPermission(context, SystemPermission.INTERNET)
            && PermissionUtil.hasPermission(context, SystemPermission.GET_NETWORK_INFO)) {
            NetManager cm = NetManager.getInstance(context);
            WifiDevice w = WifiDevice.getInstance(context);
            if (cm != null && w != null) {
                NetHandle netInfo = cm.getDefaultNet();
                return (netInfo != null && cm.hasDefaultNet() && netInfo.getNetHandle() != 0) || w.isConnected();
            }
        }
        return false;
    }

    /**
     * Gets ip address v4.
     *
     * @return the ip address
     */
    public final String getIPv4Address() {
        String result = null;
        try {
            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface intf : interfaces) {
                List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
                for (InetAddress addr : addrs) {
                    if (!addr.isLoopbackAddress()) {
                        String sAddr = addr.getHostAddress().toUpperCase(Locale.getDefault());
                        boolean isIPv4 = addr instanceof Inet4Address;
                        if (isIPv4) {
                            result = sAddr;
                        }
                    }
                }
            }
        } catch (SocketException e) {
            if (EasyDeviceInfo.debuggable) {
                HiLog.error(EasyDeviceInfo.LABEL, "", SOCKET_EXCEPTION + e);
            }
        }
        return CheckValidityUtil.checkValidData(result);
    }

    /**
     * Gets ip address v6.
     *
     * @return the ip address
     */
    public final String getIPv6Address() {
        String result = null;
        try {
            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface intf : interfaces) {
                List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
                for (InetAddress addr : addrs) {
                    if (!addr.isLoopbackAddress()) {
                        String sAddr = addr.getHostAddress().toUpperCase(Locale.getDefault());
                        boolean isIPv4 = addr instanceof Inet4Address;
                        if (!isIPv4) {
                            int delim = sAddr.indexOf('%'); // drop ip6 port suffix
                            result = delim < 0 ? sAddr : sAddr.substring(0, delim);
                        }
                    }
                }
            }
        } catch (SocketException e) {
            if (EasyDeviceInfo.debuggable) {
                HiLog.error(EasyDeviceInfo.LABEL, "", SOCKET_EXCEPTION + e);
            }
        }
        return CheckValidityUtil.checkValidData(result);
    }


    /**
     * Gets WiFi MAC Address
     * <p>
     * You need to declare the below permission in the manifest file to use this properly
     * <p>
     * SystemPermission.GET_NETWORK_INFO
     *
     * @return the wifi mac
     */
    @NeedsPermission(SystemPermission.GET_NETWORK_INFO)
    public final String getWifiMAC() {
        String result = "02:00:00:00:00:00";
        if (PermissionUtil.hasPermission(context, SystemPermission.GET_NETWORK_INFO)) {
            Enumeration<NetworkInterface> interfaces = null;
            try {
                interfaces = NetworkInterface.getNetworkInterfaces();
            } catch (SocketException e) {
                if (EasyDeviceInfo.debuggable) {
                    HiLog.error(EasyDeviceInfo.LABEL, "", SOCKET_EXCEPTION + e);
                }
            }
            while (interfaces != null && interfaces.hasMoreElements()) {
                NetworkInterface networkInterface = interfaces.nextElement();

                byte[] addr = new byte[0];
                try {
                    addr = networkInterface.getHardwareAddress();
                } catch (SocketException e) {
                    if (EasyDeviceInfo.debuggable) {
                        HiLog.error(EasyDeviceInfo.LABEL, "", SOCKET_EXCEPTION + e);
                    }
                }
                if (addr == null || addr.length == 0) {
                    continue;
                }
                StringBuilder buf = new StringBuilder();
                for (byte b : addr) {
                    buf.append(String.format("%02X:", b));
                }
                if (buf.length() > 0) {
                    buf.deleteCharAt(buf.length() - 1);
                }
                String mac = buf.toString();
                String wifiInterfaceName = "wlan0";
                result = wifiInterfaceName.equals(networkInterface.getName()) ? mac : result;
            }
        }
        return CheckValidityUtil.checkValidData(result);
    }

    /**
     * Gets SSID of Connected WiFi
     * <p>
     * You need to declare the below permission in the manifest file to use this properly
     * <p>
     * SystemPermission.GET_WIFI_INFO
     * SystemPermission.GET_NETWORK_INFO
     *
     * @return Returns the service set identifier (SSID) of the current 802.11 network
     */
    @NeedsPermission(all = {
        SystemPermission.GET_WIFI_INFO, SystemPermission.GET_NETWORK_INFO
    })
    public final String getWifiSSID() {
        String result = null;
        if (PermissionUtil.hasPermission(context, SystemPermission.GET_WIFI_INFO)) {
            WifiDevice wifiDevice = WifiDevice.getInstance(context);

            if (wifiDevice != null && wifiDevice.isConnected()) {
                Optional<WifiLinkedInfo> wifiLinked = wifiDevice.getLinkedInfo();
                if (wifiLinked.isPresent()) {
                    WifiLinkedInfo wifiLinkedInfo = wifiLinked.get();
                    if (wifiLinkedInfo != null && !TextTool.isNullOrEmpty(wifiLinkedInfo.getSsid())) {
                        result = wifiLinkedInfo.getSsid();
                    }
                }
            }
        }
        return CheckValidityUtil.checkValidData(result);
    }

    /**
     * Gets BSSID of Connected WiFi
     * <p>
     * You need to declare the below permission in the manifest file to use this properly
     * <p>
     * SystemPermission.GET_WIFI_INFO
     * SystemPermission.GET_NETWORK_INFO
     *
     * @return Return the basic service set identifier (BSSID) of the current access point.
     */
    @NeedsPermission(all = {
        SystemPermission.GET_WIFI_INFO, SystemPermission.GET_NETWORK_INFO
    })
    public final String getWifiBSSID() {
        String result = null;
        if (PermissionUtil.hasPermission(context, SystemPermission.GET_WIFI_INFO)) {
            WifiDevice wifiDevice = WifiDevice.getInstance(context);

            if (wifiDevice != null && wifiDevice.isConnected()) {
                Optional<WifiLinkedInfo> wifiLinked = wifiDevice.getLinkedInfo();
                if (wifiLinked.isPresent()) {
                    WifiLinkedInfo wifiLinkedInfo = wifiLinked.get();
                    if (wifiLinkedInfo != null && !TextTool.isNullOrEmpty(wifiLinkedInfo.getBssid())) {
                        result = wifiLinkedInfo.getBssid();
                    }
                }
            }
        }
        return CheckValidityUtil.checkValidData(result);
    }

    /**
     * Gets Link Speed of Connected WiFi
     * <p>
     * You need to declare the below permission in the manifest file to use this properly
     * <p>
     * SystemPermission.GET_WIFI_INFO
     * SystemPermission.GET_NETWORK_INFO
     *
     * @return link speed
     */
    @NeedsPermission(all = {
        SystemPermission.GET_WIFI_INFO, SystemPermission.GET_NETWORK_INFO
    })
    public final String getWifiLinkSpeed() {
        String result = null;
        if (PermissionUtil.hasPermission(context, SystemPermission.GET_WIFI_INFO)) {
            WifiDevice wifiDevice = WifiDevice.getInstance(context);

            if (wifiDevice != null && wifiDevice.isConnected()) {
                Optional<WifiLinkedInfo> wifiLinked = wifiDevice.getLinkedInfo();
                if (wifiLinked.isPresent()) {
                    WifiLinkedInfo wifiLinkedInfo = wifiLinked.get();
                    if (wifiLinkedInfo != null && !TextTool.isNullOrEmpty(wifiLinkedInfo.getSsid())) {
                        result = wifiLinkedInfo.getLinkSpeed() + " Mbps";
                    }
                }
            }
        }
        return CheckValidityUtil.checkValidData(result);
    }

    /**
     * Gets network type.
     * <p>
     * You need to declare the below permission in the manifest file to use this properly
     * <p>
     * SystemPermission.GET_NETWORK_INFO
     * SystemPermission.INTERNET
     *
     * @return the network type
     */
    @NeedsPermission(all = {
        SystemPermission.GET_NETWORK_INFO, SystemPermission.INTERNET
    })
    @NetworkType
    public final int getNetworkType() {
        int result = NetworkType.UNKNOWN;
        if (PermissionUtil.hasPermission(context, SystemPermission.GET_NETWORK_INFO)) {
            WifiDevice wifiDevice = WifiDevice.getInstance(context);

            if (wifiDevice != null && wifiDevice.isConnected()) {
                result = NetworkType.WIFI_WIFIMAX;
            } else {
                RadioInfoManager r = RadioInfoManager.getInstance(context);
                SimInfoManager simInfoManager = SimInfoManager.getInstance(context);
                if (simInfoManager != null && simInfoManager.getSimState(simInfoManager.getDefaultVoiceSlotId()) == TelephonyConstants.SIM_STATE_READY) {
                    List<SignalInformation> list = r.getSignalInfoList(simInfoManager.getDefaultVoiceSlotId());
                    if (list != null && list.get(0) != null) {
                        SignalInformation signalInformation = list.get(0);
                        switch (signalInformation.getNetworkType()) {
                            case TelephonyConstants.NETWORK_TYPE_UNKNOWN:
                                result = NetworkType.CELLULAR_UNKNOWN;
                                break;
                            case TelephonyConstants.NETWORK_TYPE_GSM:
                                result = NetworkType.CELLULAR_2G;
                                break;
                            case TelephonyConstants.NETWORK_TYPE_CDMA:
                            case TelephonyConstants.NETWORK_TYPE_WCDMA:
                            case TelephonyConstants.NETWORK_TYPE_TDSCDMA:
                                result = NetworkType.CELLULAR_3G;
                                break;
                            case TelephonyConstants.NETWORK_TYPE_LTE:
                                result = NetworkType.CELLULAR_4G;
                                break;
                            case TelephonyConstants.NETWORK_TYPE_NR:
                                result = NetworkType.CELLULAR_5G;
                                break;
                            default:
                                result = NetworkType.CELLULAR_UNIDENTIFIED_GEN;
                                break;
                        }
                    }
                }
            }
        }
        return result;
    }

    /**
     * 判断是否有wifi
     *
     * @return true if a Wi-Fi Aware compatible chipset is present in the device.
     */
    public final boolean isWifiAwareAvailable() {
        WifiDevice wifiDevice = WifiDevice.getInstance(context);
        return wifiDevice.isFeatureSupported(WifiUtils.WIFI_FEATURE_AWARE);
    }
}
